﻿using System.Collections;
using System.Data.Linq;
using System.Linq;
using MemoDb.Tests.Model;
using MemoDb.Tests.Model.Collections;
using NUnit.Framework;

namespace MemoDb.Tests
{
    [TestFixture]
    public class CascadeUpdateTests
    {
        [Test]
        public void ShouldUpdateCollectionIfConfiguredByDefault()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<Parent<Child[]>>()
                            .Map<Child>();

            var child = new Child
                            {
                                Id = 1,
                                Name = "child",
                            };

            var parent = new Parent<Child[]>
                             {
                                 Id = 1,
                                 Name = "parent"
                             };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Child[]> parent2;
            
            using (var s = memo.CreateSession())
            {
                parent2 = s.GetById<Parent<Child[]>>(parent.Id);
                parent2.Children = new[] { child };
                s.Flush();
            }

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Child[]>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Length);
            Assert.AreNotEqual(child, parent2.Children.Single());
            Assert.AreEqual(child2, parent2.Children.Single());
        }

        [Test]
        public void ByDefaultShouldFailIfCollectionChildrenWereNotSaved()
        {
            var memo = new Memo()
                            .Map<Parent<Child[]>>()
                            .Map<Child>();

            var child = new Child
                            {
                                Id = 1,
                                Name = "child",
                            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Child[]>
            {
                Id = 1,
                Name = "parent",
                Children = new[] { child }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Child[]>>());
            }
        }

        [Test]
        public void ShouldInsertReferenceIfConfiguredByDefault()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<City>()
                            .Map<Country>();

            var argentina = new Country
            {
                Id = 1,
                Name = "Argentina"
            };
            var buenosAires = new City
            {
                Id = 1,
                Name = "Buenos Aires",
                Country = argentina
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(buenosAires);
                s.Flush();
            }

            City buenosAires2;
            Country argentina2;

            using (var s = memo.CreateSession())
            {
                buenosAires2 = s.GetById<City>(buenosAires.Id);
                argentina2 = s.GetById<Country>(argentina.Id);
            }

            Assert.AreNotEqual(argentina, buenosAires2.Country);
            Assert.AreEqual(argentina2, buenosAires2.Country);
        }

        [Test]
        public void ByDefaultShouldFailIfReferenceIsNotSaved()
        {
            var memo = new Memo()
                            .Map<City>()
                            .Map<Country>();

            var argentina = new Country
            {
                Id = 1,
                Name = "Argentina"
            };

            var uruguay = new Country
            {
                Id = 2,
                Name = "Uruguay"
            };

            var buenosAires = new City
            {
                Id = 1,
                Name = "Buenos Aires",
                Country = argentina
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(uruguay);
                s.Insert(buenosAires);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<City>());
                CollectionAssert.IsEmpty(s.Query<Country>());
            }
        }

        [Test]
        public void ShouldInsertDictionaryValuesIfConfiguredByDefault()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<Parent<Hashtable>>()
                            .Map<Child>();


            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { 1, child } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Hashtable> parent2;

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Hashtable>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Count);
            Assert.AreNotEqual(child, parent2.Children[1]);
            Assert.AreEqual(child2, parent2.Children[1]);
        }

        [Test]
        public void ByDefaultShouldFailIfDictionaryValuesWereNotSaved()
        {
            var memo = new Memo()
                            .Map<Parent<Hashtable>>()
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { 1, child } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Hashtable>>());
            }
        }

        [Test]
        public void ShouldInsertDictionaryKeysIfConfiguredByDefault()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<Parent<Hashtable>>()
                            .Map<Child>();


            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { child, 1 } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Hashtable> parent2;

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Hashtable>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Count);
            Assert.AreNotEqual(child, parent2.Children.Keys.Cast<Child>().Single());
            Assert.AreEqual(child2, parent2.Children.Keys.Cast<Child>().Single());
        }

        [Test]
        public void ByDefaultShouldFailIfDictionaryKeysWereNotSaved()
        {
            var memo = new Memo()
                            .Map<Parent<Hashtable>>()
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { child, 1 } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Hashtable>>());
            }
        }

        [Test]
        public void ShouldInsertReferenceIfConfiguredExplicitly()
        {
            var memo = new Memo()
                            .Map<City>(c => c.CasacadeInsert(x => x.Country))
                            .Map<Country>();

            var argentina = new Country
            {
                Id = 1,
                Name = "Argentina"
            };
            var buenosAires = new City
            {
                Id = 1,
                Name = "Buenos Aires",
                Country = argentina
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(buenosAires);
                s.Flush();
            }

            City buenosAires2;
            Country argentina2;

            using (var s = memo.CreateSession())
            {
                buenosAires2 = s.GetById<City>(buenosAires.Id);
                argentina2 = s.GetById<Country>(argentina.Id);
            }

            Assert.AreNotEqual(argentina, buenosAires2.Country);
            Assert.AreEqual(argentina2, buenosAires2.Country);
        }

        [Test]
        public void ShouldFailIfReferenceIsNotSavedAndItWasConfiguredExplicitly()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<City>(c => c.CasacadeInsert(x => x.Country, false))
                            .Map<Country>();

            var argentina = new Country
            {
                Id = 1,
                Name = "Argentina"
            };

            var uruguay = new Country
            {
                Id = 2,
                Name = "Uruguay"
            };

            var buenosAires = new City
            {
                Id = 1,
                Name = "Buenos Aires",
                Country = argentina
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(uruguay);
                s.Insert(buenosAires);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<City>());
                CollectionAssert.IsEmpty(s.Query<Country>());
            }
        }


        [Test]
        public void ShouldInsertCollectionIfConfiguredExplicitly()
        {
            var memo = new Memo()
                            .Map<Parent<Child[]>>( c=>c.CasacadeInsert(x=>x.Children))
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var parent = new Parent<Child[]>
            {
                Id = 1,
                Name = "parent",
                Children = new[] { child }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Child[]> parent2;

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Child[]>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Length);
            Assert.AreNotEqual(child, parent2.Children.Single());
            Assert.AreEqual(child2, parent2.Children.Single());
        }

        [Test]
        public void ShouldFailIfCollectionChildrenWereNotSavedAndItWasConfiguredExplicitly()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                            .Map<Parent<Child[]>>(c => c.CasacadeInsert(x => x.Children,false))
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Child[]>
            {
                Id = 1,
                Name = "parent",
                Children = new[] { child }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Child[]>>());
            }
        }





        [Test]
        public void ShouldInsertDictionaryValuesIfConfiguredExplicitly()
        {
            var memo = new Memo()
                            .Map<Parent<Hashtable>>(c => c.CasacadeInsert(x => x.Children))
                            .Map<Child>();


            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { 1, child } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Hashtable> parent2;

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Hashtable>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Count);
            Assert.AreNotEqual(child, parent2.Children[1]);
            Assert.AreEqual(child2, parent2.Children[1]);
        }

        [Test]
        public void ShouldFailIfDictionaryValuesWereNotSavedAndItWasConfiguredExplicitly()
        {
            var memo = new Memo(insertByCascadeByDefault:true)
                            .Map<Parent<Hashtable>>(c => c.CasacadeInsert(x => x.Children,false))
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { 1, child } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Hashtable>>());
            }
        }

        [Test]
        public void ShouldInsertDictionaryKeysIfConfiguredExplicitly()
        {
            var memo = new Memo()
                            .Map<Parent<Hashtable>>(c => c.CasacadeInsert(x => x.Children))
                            .Map<Child>();


            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { child, 1 } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(parent);
                s.Flush();
            }

            Child child2;
            Parent<Hashtable> parent2;

            using (var s = memo.CreateSession())
            {
                child2 = s.GetById<Child>(child.Id);
                parent2 = s.GetById<Parent<Hashtable>>(parent.Id);
            }

            Assert.AreEqual(1, parent2.Children.Count);
            Assert.AreNotEqual(child, parent2.Children.Keys.Cast<Child>().Single());
            Assert.AreEqual(child2, parent2.Children.Keys.Cast<Child>().Single());
        }

        [Test]
        public void ShouldFailIfDictionaryKeysWereNotSavedAndItWasConfiguredExplicitly()
        {
            var memo = new Memo(insertByCascadeByDefault:true)
                            .Map<Parent<Hashtable>>(c => c.CasacadeInsert(x => x.Children, false))
                            .Map<Child>();

            var child = new Child
            {
                Id = 1,
                Name = "child",
            };

            var child2 = new Child
            {
                Id = 2,
                Name = "child2",
            };

            var parent = new Parent<Hashtable>
            {
                Id = 1,
                Name = "parent",
                Children = new Hashtable { { child, 1 } }
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(child2);
                s.Insert(parent);
                Assert.Throws<MemoDbException>(s.Flush);
            }

            using (var s = memo.CreateSession())
            {
                CollectionAssert.IsEmpty(s.Query<Child>());
                CollectionAssert.IsEmpty(s.Query<Parent<Hashtable>>());
            }
        }

        [Test]
        public void EntityRef()
        {
            var memo = new Memo(insertByCascadeByDefault: true)
                        .Map<CityWithEntityRef>()
                        .Map<Country>();

            var argentina = new Country
            {
                Id = 1,
                Name = "Argentina"
            };
            var buenosAires = new CityWithEntityRef
            {
                Id = 1,
                Name = "Buenos Aires",
                Country = new EntityRef<Country>(argentina)
            };

            using (var s = memo.CreateSession())
            {
                s.Insert(buenosAires);
                s.Flush();
            }

            CityWithEntityRef buenosAires2;
            Country argentina2;

            using (var s = memo.CreateSession())
            {
                buenosAires2 = s.GetById<CityWithEntityRef>(buenosAires.Id);
                argentina2 = s.GetById<Country>(argentina.Id);
            }

            Assert.AreNotEqual(argentina, buenosAires2.Country.Entity);
            Assert.AreEqual(argentina2, buenosAires2.Country.Entity);
        }
    }
}